package com.citcc.api.controller;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.citcc.api.config.Authorization;
import com.citcc.api.netty.bean.ConsuMessage;
import com.citcc.api.netty.server.ChannelMap;
import com.citcc.api.netty.server.ServerToClient;
import com.citcc.api.utils.PaySystemUtil;
import com.citcc.api.utils.WeiXinPayUtil;
import com.citcc.business.bean.*;
import com.citcc.business.bean.dto.CdOrderDto;
import com.citcc.business.bean.dto.PayDTO;
import com.citcc.business.bean.vo.CdzOrderVo;
import com.citcc.business.bean.vo.CdzSFVo;
import com.citcc.business.service.*;
import com.citcc.business.util.IdGenerator;
import com.citcc.common.base.AjaxResult;
import com.citcc.common.jpush.PushUtil;
import com.citcc.common.utils.StringUtils;
import com.citcc.framework.base.BaseController;
import com.citcc.system.service.impl.SysDictDataServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.lang3.time.FastDateFormat;
import org.jboss.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: qinbo
 * Date: 2019/4/7
 * Time: 14:26
 * Description:
 */
@RestController
@Api(tags = "订单支付")
public class PayController extends BaseController {
    private Logger logger = LoggerFactory.getLogger(PayController.class);
    
    static FastDateFormat format = FastDateFormat.getInstance("yyyyMMddHHmmss");
    @Value("${pay.zhifubao.apppay.appid}")
    private String appid;
    @Value("${pay.zhifubao.apppay.privateKey}")
    private String privateKey;
    @Value("${pay.zhifubao.apppay.publicKey}")
    private String publicKey;
    @Value("${pay.zhifubao.apppay.requesturl}")
    private String requesturl;
    @Value("${pay.zhifubao.apppay.returnurl}")
    private String returnUrl;
    @Value("${pay.zhifubao.apppay.partnerid}")
    private String partnerid;
    @Value("${pay.weixin.apppay.appid}")
    private String wxappid;
    @Value("${pay.weixin.apppay.partner}")
    private String MCH_ID;
    @Value("${pay.weixin.apppay.returnurl}")
    private String WX_NOTIFY;
    public String timeMillis = String.valueOf(System.currentTimeMillis() / 1000);

    @Autowired
    private CdzStationService cdzStationService;

    @Autowired
    private CdzRatesService cdzRatesService;

    @Autowired
    private TCdzTimeTypeService cdzTimeTypeService;

    @Autowired
    private CdzStakeService cdzStakeService;

    @Autowired
    private ScanCodeService scanCodeService;
    @Autowired
    private SysDictDataServiceImpl sysDictDataService;
    @Autowired
    private CdzAccountAmountService cdzAccountAmountService;
    @Autowired
    private CdzAmountDetailService cdzAmountDetailService;
    @Autowired
    private ServerToClient serverToClient;
    @Autowired
    private CdzAppUserService cdzAppUserService;
    @Autowired
    private CdzPushService cdzPushService;

    String randomString = WeiXinPayUtil.getRandomString(32);

    /**
     * DecimalFormat 内部静态方法较多，设置单例
     */
    private static class DecimalFormatProvider {
        private static final DecimalFormat format = new DecimalFormat("#,##0.00");
    }

    public static DecimalFormat getFormat() {
        return DecimalFormatProvider.format;
    }

    /**
     * 查询费用说明
     *
     * @param cdOrderDto
     * @return
     */
    @ApiOperation("查询费用说明")
    @RequestMapping(value = "/v1/order/type",
            method = RequestMethod.POST)
    public Object chooseType(CdOrderDto cdOrderDto) {
        CdzIndent cdzIndent = new CdzIndent();
        CdzStation cdzStation = cdzStationService.getCdzStation(cdOrderDto.getCdzCode());
        if (cdzStation == null) {
            return error(-1, "未查询到充电站相关信息");
        } else {
            DecimalFormat fomart = new DecimalFormat("#,##0.00");
            String rates = cdzStation.getRates();
            CdzRates cdzRates = cdzRatesService.getCdzRates(rates);
            CdzSFVo cdzSFVo = new CdzSFVo();
            cdzSFVo.setId(cdzRates.getId());
            cdzSFVo.setName(cdzRates.getName());
            cdzSFVo.setDirectElectric(cdzRates.getDirectElectric() == null ? "0.00" : fomart.format(cdzRates.getDirectElectric()));
            cdzSFVo.setServe(cdzRates.getServe() == null ? "0.00" : fomart.format(cdzRates.getServe()));
            cdzSFVo.setPark(cdzRates.getPark() == null ? "0.00" : fomart.format(cdzRates.getPark()));
            cdzSFVo.setAlternatingElectric(cdzRates.getAlternatingElectric() == null ? "0.00" : fomart.format(cdzRates.getAlternatingElectric()));
            return success(200, cdzSFVo);
        }
    }

    @ApiOperation("获取充电模式对应值")
    @RequestMapping(value = "/v1/pay/type/time", method = RequestMethod.POST)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "chargeMs", value = "充电模式（0-自动充满，1-按金额充电，2-按时间充电，3-按电量充电）", paramType = "query")
    })
    public Object getType(@RequestParam String chargeMs) {
        TCdzTimeType tCdzTimeType = new TCdzTimeType();
        tCdzTimeType.setDelFlag("0");
        tCdzTimeType.setChargeMs(chargeMs);
        List<TCdzTimeType> tCdzTimeTypes = cdzTimeTypeService.selectTCdzTimeTypeList(tCdzTimeType);
        Map<String, Object> data = new HashedMap<>();
        data.put("list", tCdzTimeTypes);
        switch (chargeMs){
            case "1":
                data.put("start", 10);
                data.put("end", 200);
                data.put("unit", "元");
                break;
            case "2":
                data.put("start", 1);
                data.put("end", 9);
                data.put("unit", "小时");
                break;
            case "3":
                data.put("start", 10);
                data.put("end", 150);
                data.put("unit", "度");
                break;
        }
        return success(200, data);
    }

    /**
     * 生成订单
     *
     * @return
     */
    @ApiOperation("生成订单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "type", value = "0：服务，1：钱包", paramType = "query"),
            @ApiImplicitParam(name = "gunCode", value = "充电枪编号", paramType = "query"),
            @ApiImplicitParam(name = "chargingLong", value = "充电时长", paramType = "query"),
            @ApiImplicitParam(name = "stakeNum", value = "充电桩编号", paramType = "query"),
            @ApiImplicitParam(name = "chargeMs", value = "充电模式（0-自动充满，1-按金额充电，2-按时间充电，3-按电量充电）", paramType = "query"),
            @ApiImplicitParam(name = "value", value = "对应值", paramType = "query")
    })
    @RequestMapping(value = "/v1/pay/create/order",
            method = RequestMethod.POST)
    public Object productOrder(@Authorization CdzAppUser cdzAppUser, CdzSFVo cdzSFVo) {
        logger.info("生成订单" + cdzSFVo);
        if (StringUtils.isEmpty(cdzSFVo.getChargeMs())) {
            return error(-1, "请选择充电模式");
        }

        CdzOrderVo cdzOrderVo = new CdzOrderVo();
        cdzOrderVo.setChargeMs(cdzSFVo.getChargeMs());
        //查询充电枪
        CdzGun cdzGun = scanCodeService.checkCdzGun(cdzSFVo.getGunCode(), cdzSFVo.getStakeNum());
        if (cdzGun == null) {
            logger.info("未查询到对应充电枪");
            return error(-1, "未查询到对应充电枪");
        }

        //查询充电桩
        CdzStake cdzStake = cdzStakeService.getCdzStakeByStakeCode(cdzGun.getStakeCode());
        if (cdzStake == null) {
            logger.info("未获取到对应桩");
            return error(-1, "未获取到对应桩");
        }
        //根据枪号查询站；
        CdzStation cdzStation = cdzStationService.getByGunIdAndStakeCode(cdzSFVo.getGunCode(), cdzSFVo.getStakeNum());
        if (cdzStation == null) {
            return error(-1, "未查询到对应站点");
        }
        //1:直流 2:交流 3:混合
        String type = cdzStation.getType();
        String rates = cdzStation.getRates();
        CdzRates cdzRates = cdzRatesService.getCdzRates(rates);
        if (cdzRates == null) {
            return error(-1, "未查询到对应缴费模式");
        }
        //直流
        BigDecimal directElectric = new BigDecimal(0);
        BigDecimal alternatingElectric = new BigDecimal(0);
        BigDecimal serve = new BigDecimal(0);
        BigDecimal park = new BigDecimal(0);
        switch (type) {
            case "1":
                //直流充电
                //每小时
                directElectric = cdzRates.getDirectElectric();
                //服务
                serve = cdzRates.getServe();
                //停车
                park = cdzRates.getPark();
                directElectric = (directElectric == null ? new BigDecimal(0) : directElectric);
                serve = (serve == null ? new BigDecimal(0) : serve);
                park = (park == null ? new BigDecimal(0) : park);
                cdzOrderVo.setChargeType("直流");
                cdzOrderVo.setCdf(getFormat().format(directElectric) + "元/度");
                cdzOrderVo.setFwf(getFormat().format(serve) + "元/度");
                cdzOrderVo.setTcf(getFormat().format(park) + "元/小时");
                break;
            case "2":
                cdzOrderVo.setChargeType("交流");
                //每小时交流
                alternatingElectric = cdzRates.getAlternatingElectric();
                //服务
                serve = cdzRates.getServe();
                //停车
                park = cdzRates.getPark();
                alternatingElectric = (alternatingElectric == null ? new BigDecimal(0) : alternatingElectric);
                serve = (serve == null ? new BigDecimal(0) : serve);
                park = (park == null ? new BigDecimal(0) : park);
                cdzOrderVo.setChargeType("直流");
                cdzOrderVo.setCdf(getFormat().format(alternatingElectric) + "元/度");
                cdzOrderVo.setFwf(getFormat().format(serve) + "元/度");
                cdzOrderVo.setTcf(getFormat().format(park) + "元/小时");
                break;
            case "3":
                cdzOrderVo.setChargeType("混合");
                break;
            default:
                break;
        }
        String money = "0";
        //按选择模式算值
        switch (cdzSFVo.getChargeMs()) {
            case "0"://自动充满
                cdzOrderVo.setMs("自动充满");
                cdzOrderVo.setMoney(150 + "元");
                money = "150";
                break;
            case "1"://按金额充电
                if (StringUtils.isEmpty(cdzSFVo.getValue())) {
                    return error(-1, "请选择充电金额");
                }
                cdzOrderVo.setMs("按金额充电");
                cdzOrderVo.setValue(cdzSFVo.getValue() + "元");
                cdzOrderVo.setMoney(cdzSFVo.getValue() + "元");
                money = cdzSFVo.getValue();
                break;
            case "2"://按时间充电
                if (StringUtils.isEmpty(cdzSFVo.getValue())) {
                    return error(-1, "请选择充电时间");
                }
                cdzOrderVo.setMs("按时间充电");
                cdzOrderVo.setValue(cdzSFVo.getValue() + "小时");
                cdzOrderVo.setMoney(150 + "元");
                money = "150";
                break;
            case "3"://按电量充电
                if (StringUtils.isEmpty(cdzSFVo.getValue())) {
                    return error(-1, "请选择充电电量");
                }
                cdzOrderVo.setMs("按电量充电");
                cdzOrderVo.setValue(cdzSFVo.getValue() + "度");
                switch (type) {
                    case "1":
                        //直流电算金额
                        double val = directElectric.add(serve).doubleValue();
                        BigDecimal electricity = new BigDecimal(cdzSFVo.getValue());
                        BigDecimal price = new BigDecimal(val);
                        //所选电量*充电单价
                        Double amount = electricity.multiply(price).doubleValue();
                        money = getFormat().format(amount);
                        cdzOrderVo.setMoney(money + "元");
                        break;
                    case "2":
                        //交流电算金额
                        double val1 = alternatingElectric.add(serve).doubleValue();
                        BigDecimal electricity1 = new BigDecimal(cdzSFVo.getValue());
                        BigDecimal price1 = new BigDecimal(val1);
                        //所选电量*充电单价
                        Double amount1 = electricity1.multiply(price1).doubleValue();
                        money = getFormat().format(amount1);
                        cdzOrderVo.setMoney(money + "元");
                        break;
                }
                break;
        }

        //额定功率
        String power = cdzStake.getPower();
        //国家标准
        String standard = cdzStake.getStandard();
        //电压
        String voltage = cdzStake.getVoltage();
        //获取服务费
        cdzOrderVo.setCdzName(cdzStation.getName());
        String gunNum = sysDictDataService.selectDictLabel("gun_code", cdzGun.getGunNum());
        cdzOrderVo.setGunNum(gunNum);
        cdzOrderVo.setStakeCode(cdzGun.getStakeCode());
        String s = sysDictDataService.selectDictLabel("stake_standard", standard);
        logger.info("标准>>>>>" + s);
        cdzOrderVo.setStandard(s);
        cdzOrderVo.setPower(StringUtils.isEmpty(power) ? null : power);
        cdzOrderVo.setDy(voltage);
        //生成订单
        String id = IdGenerator.generateId();
        UserOrderEntity userOrderEntity = new UserOrderEntity();
        userOrderEntity.setId(id);
        userOrderEntity.setUserId(cdzAppUser.getId());
        userOrderEntity.setPayState("0");
        //实付金额
        BigDecimal payNumber = new BigDecimal(money);
        userOrderEntity.setPayNumber(payNumber);
        userOrderEntity.setCreateTime(new Date());
        userOrderEntity.setCreateBy(cdzAppUser.getId());
        userOrderEntity.setUpdateTime(new Date());
        userOrderEntity.setUpdateBy(cdzAppUser.getId());
        userOrderEntity.setDelFlag("0");
        userOrderEntity.setGunCode(cdzGun.getGunNum());
        userOrderEntity.setStakeCode(cdzGun.getStakeCode());
        userOrderEntity.setStationCode(cdzStation.getNum());
        userOrderEntity.setChargeType(type);
        userOrderEntity.setChargePower(power);
        userOrderEntity.setChargeDy(voltage);
        userOrderEntity.setChargeMoney(directElectric);
        userOrderEntity.setChargeFw(serve);
        userOrderEntity.setChargeTc(park);
        userOrderEntity.setStationName(cdzStation.getName());
        userOrderEntity.setChargeMs(cdzSFVo.getChargeMs());
        userOrderEntity.setChargeStandard(s);
        userOrderEntity.setUserPhone(cdzAppUser.getPhone());
        userOrderEntity.setValue(cdzSFVo.getValue());
        userOrderEntity.setTradeNo(generateOrder());
        scanCodeService.saveOrder(userOrderEntity);
        cdzOrderVo.setCdzId(id);
        return success(200, cdzOrderVo);
    }


    /**
     * 支付
     *
     * @param request
     * @return
     */
    @ApiImplicitParams({
            @ApiImplicitParam(name = "orderNum", value = "订单号", paramType = "query"),
            @ApiImplicitParam(name = "tradeType", value = "微信APP支付：wx-app-pay  支付宝APP支付：zfb-app-pay", paramType = "query"),
    })
    @RequestMapping(value = "/v1/pay/all",
            method = RequestMethod.POST)
    @Transactional(rollbackFor = Exception.class)
    public Object AllToPay(@Authorization CdzAppUser cdzAppUser, @RequestParam String orderNum, @RequestParam String tradeType, HttpServletRequest request) {
        //生成订单

        PayDTO payDTO = new PayDTO();
        UserOrderEntity userOrderEntity = scanCodeService.getCdOrder(orderNum);
        logger.info("订单= " + userOrderEntity);
        if (userOrderEntity == null) {
            return error(-1, "订单环境异常");
        }
        if ("1".equals(userOrderEntity.getDelFlag()) || !"0".equals(userOrderEntity.getPayState())) {
            return error(-1, "订单禁止下单");
        }
        String orderNo = IdGenerator.generateId();
        if (userOrderEntity.getPayNumber() == null) {
            return error(-1, "金额环境异常");
        }


        switch (tradeType) {
            case "wx-app-pay":
                payDTO.setPaymoney(userOrderEntity.getPayNumber());
                // payDTO.setPaymoney(userOrderEntity.getPayNumber());
                payDTO.setOut_trade_no(orderNo);
                payDTO.setAppid(wxappid);
                payDTO.setMch_id(MCH_ID);
                payDTO.setUserId(cdzAppUser.getId());
                payDTO.setDescription("新能源车充电");
                payDTO.setWxnotifyURL(WX_NOTIFY);
                payDTO.setReward("wx-app-pay");
                payDTO.setUserId(cdzAppUser.getId());
                try {
                    SortedMap<String, Object> map = wxCreateOrder(request, payDTO);

                    logger.info("微信支付app唤醒成功" + map);
                    //修改支付状态
                    userOrderEntity.setId(orderNum);
                    userOrderEntity.setType(tradeType);
                    userOrderEntity.setPayTime(new Date());
                    userOrderEntity.setOutTradeNo(orderNo);
                    userOrderEntity.setPayState("1");
                    userOrderEntity.setPayType("1");
                    boolean up = scanCodeService.updatePayState(userOrderEntity);
                    if (up) {
                        logger.info("微信支付app唤醒成功" + map);
                        return success(200, map);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.info(e.getMessage());
                    return error(-1, "支付失败");
                }
                return error(-1, "支付失败");

            case "zfb-app-pay":

                //  payDTO.setPaymoney(userOrderEntity.getPayNumber());
                payDTO.setPaymoney(userOrderEntity.getPayNumber());
                payDTO.setOut_trade_no(orderNo);
                payDTO.setReward(tradeType);
                payDTO.setUserId(cdzAppUser.getId());

                try {
                    Map<String, Object> map = zfbCreateOrder(payDTO);
                    //修改支付状态
                    userOrderEntity.setId(orderNum);
                    userOrderEntity.setType(tradeType);
                    userOrderEntity.setPayTime(new Date());
                    userOrderEntity.setOutTradeNo(orderNo);
                    userOrderEntity.setPayState("1");
                    userOrderEntity.setPayType("0");
                    boolean up = scanCodeService.updatePayState(userOrderEntity);
                    if (up) {
                        logger.info("支付宝支付app唤醒成功" + map);
                        return success(200, map);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return error(-1, "唤醒失败");
            default:
                return error(-1, "支付失败");
        }


    }

    //支付寳统一下单
    public Map<String, Object> zfbCreateOrder(PayDTO payDTO) throws Exception {

        logger.info("支付  " + payDTO);
        String orderStr = "";
        Map<String, Object> m = new HashedMap();
        DecimalFormat fomart = new DecimalFormat("0.00");
        try {
            /****** 1.封装你的交易订单开始 *****/                                        //自己用

            // 此处封装你的订单数据，订单状态可以设置为等待支付

            /****** 1.封装你的交易订单结束 *****/
            // logger.info(tradeDTO.getTradeMoney() + "---------qian");
            Map<String, String> orderMap = new LinkedHashMap<String, String>();            //订单实体
            Map<String, String> bizModel = new LinkedHashMap<String, String>();            //公共实体

            /****** 2.商品参数封装开始 *****/                                            //手机端用
            // 商户订单号，商户网站订单系统中唯一订单号，必填
            orderMap.put("out_trade_no", payDTO.getOut_trade_no());
            // 订单名称，必填
            orderMap.put("subject", "新能源车充电");
            // 付款金额，必填
            orderMap.put("total_amount", fomart.format(payDTO.getPaymoney()) + "");
            // 商品描述，可空
            orderMap.put("body", "充电费用" + fomart.format(payDTO.getPaymoney()) + "元");
            // 超时时间 可空
            orderMap.put("timeout_express", "30m");
            // 销售产品码 必填
            orderMap.put("product_code", "QUICK_WAP_PAY");
            orderMap.put("goods_type", "1");
            //业务数据

            /****** 2.商品参数封装结束 *****/
         /*   Map<String, String> attachmap=new HashedMap();
            attachmap.put("reward",reward);
            attachmap.put("userId",userId);*/
            orderMap.put("passback_params", payDTO.getReward() + "_" + URLEncoder.encode(payDTO.getUserId(), "utf-8"));

            /******--------------- 3.公共参数封装 开始 ------------------------*****/        //支付宝用
            //1.商户appid
            bizModel.put("app_id", appid);
            //2.请求网关地址
            bizModel.put("method", requesturl);
            //3.请求格式
            bizModel.put("format", "json");
            //4.回调地址
            bizModel.put("return_url", returnUrl);
            //5.私钥
            bizModel.put("private_key", privateKey);
            //6.商家id
            bizModel.put("seller_id", partnerid);
            //7.加密格式
            bizModel.put("sign_type", "RSA2");


            /******--------------- 3.公共参数封装 结束 ------------------------*****/

            //实例化客户端
            AlipayClient client = new DefaultAlipayClient(requesturl, appid, privateKey, "json", "UTF-8", publicKey, "RSA2");

            //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
            AlipayTradeAppPayRequest ali_request = new AlipayTradeAppPayRequest();

            //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
            model.setPassbackParams(URLEncoder.encode((String) orderMap.get("body").toString()));
            //描述信息  添加附加数据
            model.setBody(orderMap.get("body"));                        //商品信息
            model.setSubject(orderMap.get("subject"));                  //商品名称
            model.setOutTradeNo(orderMap.get("out_trade_no"));          //商户订单号(自动生成)
            model.setTimeoutExpress(orderMap.get("timeout_express"));     //交易超时时间
            model.setTotalAmount(orderMap.get("total_amount"));         //支付金额
            model.setProductCode(orderMap.get("product_code"));         //销售产品码
            model.setSellerId(partnerid);
            //商家id
            model.setPassbackParams(orderMap.get("passback_params"));
            model.setGoodsType(orderMap.get("goods_type"));
            //fenqi
           /* ExtendParams extendParams = new ExtendParams();
            extendParams.setHbFqNum("3");
            extendParams.setHbFqSellerPercent("100");
            model.setExtendParams(extendParams);*/
            ali_request.setBizModel(model);
            ali_request.setNotifyUrl(returnUrl);          //回调地址

            AlipayTradeAppPayResponse response = client.sdkExecute(ali_request);
            orderStr = response.getBody();
            System.err.println(orderStr);                                //就是orderString 可以直接给客户端请求，无需再做处理。
            m.put("result", orderStr);
        } catch (Exception e) {

            e.printStackTrace();
        }
        return m;
    }


    //微信统一下单
    public SortedMap<String, Object> wxCreateOrder(HttpServletRequest request, PayDTO payDTO) throws Exception {
        logger.info("支付  " + payDTO);
        //微信统一下单
        System.out.println(payDTO.getWxnotifyURL());
        //附加参数
        Map<String, String> attachmap = new HashedMap();
        attachmap.put("userId", payDTO.getUserId());
        attachmap.put("reward", payDTO.getReward());
        Map<String, String> map = PaySystemUtil.WeiXinPrePay(payDTO, request, JSONObject.toJSONString(attachmap));
        SortedMap<String, Object> finalpackage = new TreeMap<String, Object>();
        finalpackage.put("appid", payDTO.getAppid());
        finalpackage.put("timestamp", timeMillis);
        finalpackage.put("noncestr", IdGenerator.generateId());
        finalpackage.put("package", "Sign=WXPay");
        finalpackage.put("partnerid", payDTO.getMch_id());
        finalpackage.put("prepayid", map.get("prepay_id"));
        String sign = WeiXinPayUtil.createSign("UTF-8", finalpackage);
        finalpackage.put("paySign", sign);
        return finalpackage;
    }

    /**
     * @param transaction_id
     * @param refund_amount  需要退款的金额，该金额不能大于订单金额,单位为元，支持两位小数
     * @return
     * @throws
     * @方法名称:alipayRefundRequest
     * @内容摘要: ＜支付宝退款请求＞
     * @author:秦波
     * @创建日期:2016年4月12日-下午4:53:30
     */
    @ApiOperation("退款")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "type", value = "wx：微信，zfb：支付宝", paramType = "query"),
            @ApiImplicitParam(name = "transaction_id", value = "退款金额", paramType = "query"),
            @ApiImplicitParam(name = "id", value = "退款请求id", paramType = "query"),
    })
    @RequestMapping(value = "/pay/drawback", method = RequestMethod.POST)
    public Object alipayRefundRequest(@RequestParam String type, @RequestParam String transaction_id, @RequestParam BigDecimal pay_amount, @RequestParam BigDecimal refund_amount, @RequestParam String id) {

        // 发送请求
        String strResponse = null;
        PayResult payResult = new PayResult();
        if ("wx".equals(type)) {
            Map<String, String> stringStringMap = this.doRefund(transaction_id, pay_amount, refund_amount, id);
            logger.info(stringStringMap.get("return_code"));
            logger.info(stringStringMap.get("return_msg"));
            payResult.setCode(stringStringMap.get("return_code"));
            payResult.setMsg(stringStringMap.get("return_msg"));
            if ("SUCCESS".equals(stringStringMap.get("return_code"))) {
                payResult.setSub_code(stringStringMap.get("err_code"));
                payResult.setSub_msg(stringStringMap.get("err_code_des"));
                payResult.setOut_trade_no(stringStringMap.get("out_trade_no"));
                payResult.setTrade_no(stringStringMap.get("transaction_id"));
                payResult.setRefund_amount(stringStringMap.get("total_fee"));
                payResult.setOut_refund_no(stringStringMap.get("out_refund_no"));
                payResult.setIn_refund_no(stringStringMap.get("refund_id"));
                return success(200, payResult);
            } else {
                return error(-1, "退款失败，状态码" + payResult.getCode() + "描述：" + payResult.getMsg());
            }
        } else if ("zfb".equals(type)) {
            try {
                AlipayClient alipayClient = new DefaultAlipayClient
                        (requesturl, appid,
                                privateKey, "json", "GBK", publicKey, "RSA2");
                AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
                AlipayRefundInfo alidata = new AlipayRefundInfo();
                alidata.setRefund_amount(refund_amount);
                alidata.setTrade_no(transaction_id);
                alidata.setOut_request_no(id);
                request.setBizContent(JSONObject.toJSONString(alidata));
                AlipayTradeRefundResponse response = alipayClient.execute(request);
                strResponse = response.getCode();
                logger.info("支付宝账号：" + response.getBuyerLogonId());
                logger.info(strResponse);
                payResult.setMsg(response.getMsg());
                payResult.setCode(response.getCode());
                payResult.setSub_code(response.getSubCode());
                payResult.setSub_msg(response.getSubMsg());
                if ("10000".equals(response.getCode())) {
                    strResponse = "退款成功";
                    payResult.setOut_trade_no(response.getOutTradeNo());
                    payResult.setTrade_no(response.getTradeNo());
                    payResult.setRefund_amount(response.getRefundFee());
                    return success(200, payResult);
                } else {
                    logger.info("退款失败");
                    return error(-1, "退款失败，状态码" + payResult.getCode() + "描述：" + payResult.getMsg());
                }
            } catch (Exception e) {
                logger.info(e.getMessage());
                logger.info("退款失败");
                return error(-1, "退款失败");
            }
        } else {
            return error(-1, null);
        }
    }


    /**
     * 退款參數封装
     */
    public Map<String, String> doRefund(String transaction_id, BigDecimal pay_amount, BigDecimal refund_amount, String id) {

        logger.info("------------封装");
        SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
        parameterMap.put("appid", WeiXinPayUtil.APPID);
        parameterMap.put("mch_id", WeiXinPayUtil.MCH_ID);
        parameterMap.put("nonce_str", randomString);
        parameterMap.put("transaction_id", transaction_id);
        parameterMap.put("out_refund_no", id);
        parameterMap.put("refund_fee_type", "CNY");
        logger.info("jiner");
        BigDecimal refund = refund_amount.multiply(new BigDecimal(100));
        BigDecimal pay = pay_amount.multiply(new BigDecimal(100));
        java.text.DecimalFormat df = new java.text.DecimalFormat("0");
        parameterMap.put("total_fee", df.format(pay));
        parameterMap.put("refund_fee", df.format(refund));
        String sign = WeiXinPayUtil.createSign("UTF-8", parameterMap);
        logger.info("jiner2");
        parameterMap.put("sign", sign);
        String result = null;
        Map<String, String> map = null;
        try {
            String requestXML = WeiXinPayUtil.mapToXml(parameterMap);
            logger.info(requestXML);
            result = WeiXinPayUtil.httpsRequest2(
                    "https://api.mch.weixin.qq.com/secapi/pay/refund", "POST",
                    requestXML);
            map = WeiXinPayUtil.xmlToMap(result);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return map;
    }


    @ApiOperation("判断余额是否不足")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "value", value = "值", paramType = "query"),
            @ApiImplicitParam(name = "stakeCode", value = "桩号", paramType = "query"),
            @ApiImplicitParam(name = "chargeMs", value = "充电模式（0-自动充满，1-按金额充电，2-按时间充电，3-按电量充电）", paramType = "query")
    })
    @RequestMapping(value = "/v1/pay/balanceState", method = RequestMethod.GET)
    public AjaxResult balanceState(@Authorization CdzAppUser cdzAppUser, @RequestParam String chargeMs
            , @RequestParam String value, @RequestParam String stakeCode) {
        logger.info("chargeMs:" + chargeMs + ",userId:" + cdzAppUser.getId() + ",value:" + value + ",stakeCode:" + stakeCode);
        String chargingMoney = "";
        //按时间，自动充满，金额定为150
        switch (chargeMs) {
            case "0":
                chargingMoney = "150";
                break;
            case "1":
                chargingMoney = value;
                break;
            case "2":
                chargingMoney = "150";
                break;
            case "3":
                //根据桩号查询站号，站号拿到rates值，查询费率
                CdzStake cdzStake = cdzStakeService.getCdzStakeByStakeCode(stakeCode);
                CdzStation cdzStation = cdzStationService.getCdzStation(cdzStake.getStationId());
                if (cdzStation == null) {
                    return error(-1, "未查询到充电站相关信息");
                } else {
                    //1:直流 2:交流 3:混合
                    BigDecimal directElectric = new BigDecimal(0);
                    BigDecimal alternatingElectric = new BigDecimal(0);
                    BigDecimal serve = new BigDecimal(0);
                    String type = cdzStation.getType();
                    String rates = cdzStation.getRates();
                    CdzRates cdzRates = cdzRatesService.getCdzRates(rates);
                    if (cdzRates == null) {
                        return error(-1, "未查询到对应缴费模式");
                    }
                    switch (type) {
                        case "1":
                            //直流充电
                            //直流电每度电费用
                            directElectric = cdzRates.getDirectElectric();
                            //服务费
                            serve = cdzRates.getServe();
                            BigDecimal electricity = new BigDecimal(value);
                            //所选电量*充电单价
                            chargingMoney = String.valueOf(electricity.multiply(directElectric.add(serve)));
                            break;
                        case "2":
                            //交流电每度电费用
                            alternatingElectric = cdzRates.getAlternatingElectric();
                            //服务费
                            serve = cdzRates.getServe();
                            BigDecimal electricity1 = new BigDecimal(value);
                            //所选电量*充电单价
                            chargingMoney = String.valueOf(electricity1.multiply(alternatingElectric.add(serve)));
                            break;
                        case "3":
                            chargingMoney = "0";
                            break;
                    }
                }
                break;
            default:
                chargingMoney = "0";
                break;
        }

        //根据用户id查询总金额
        CdzAccountAmount accountAmount = cdzAccountAmountService.getAccountAmount(cdzAppUser.getId());
        Map<String, Object> map = new HashedMap<>();
        if (null != accountAmount) {
            BigDecimal bd = new BigDecimal(chargingMoney);
            //a.compareTo(b)   -1 小于   0 等于    1 大于
            if (bd.compareTo(accountAmount.getTotalAmount()) < 1) {
                map.put("state", 1);
                return success(200, map, "余额足");
            } else {
                map.put("state", 0);
                return success(200, map, "余额不足" + chargingMoney + "元，请充值！");
            }
        } else {
            map.put("state", 0);
            return success(200, map, "余额不足" + chargingMoney + "元，请充值！");
        }
    }


    @ApiOperation("确认订单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "订单id", paramType = "query")
    })
    @RequestMapping(value = "/v1/pay/verify/order", method = RequestMethod.GET)
    public AjaxResult verifyOrder(@RequestParam String id) {
        UserOrderEntity cdOrder = scanCodeService.getCdOrder(id);
        //查询充电枪
        CdzGun cdzGun = scanCodeService.checkCdzGun(cdOrder.getGunCode(), cdOrder.getStakeCode());
        if (null != cdzGun && ! "2".equals(cdzGun.getStatus())) {
            logger.info("未查询到对应充电枪" + cdzGun.getStatus());
            return error(-1, "该充电枪不可用");
        }
        //如果为true，开启指令，else返回-1
        boolean result = isopen(cdOrder.getStakeCode());
        logger.info("通道是否可用========="+result);
        if (result){
            //更新订单状态和枪状态
            //充电状态（0:充电中 1:未充电 2:已完成,3:关闭）
            cdOrder.setChargeState("1");
            //支付状态（0：未冻结，1：已冻结 2：成功 3：失败）
            cdOrder.setPayState("0");
            Long time=System.currentTimeMillis();
            cdOrder.setStartTime(new Date(time));
            scanCodeService.updatePayStateAndGun(cdOrder);

            //开启充电指令
            ConsuMessage consuMessage = new ConsuMessage();
            consuMessage.setDeviceNo(Long.parseLong(cdOrder.getStakeCode()));
            consuMessage.setSource(Integer.parseInt(cdOrder.getGunCode()));
            consuMessage.setPhone(cdOrder.getUserPhone());
            consuMessage.setMode(cdOrder.getChargeMs());
            consuMessage.setId(id);
            //根据模式 开启充电指令 1：金额模式  单位分，2：时间模式，单位为秒 3：电量，单位为0.01kw/h
            switch (cdOrder.getChargeMs()) {
                case "1":
                    BigDecimal val = new BigDecimal(cdOrder.getValue());
                    consuMessage.setMoney(val.multiply(new BigDecimal(100)).longValue());
                    break;
                case "2":
                    BigDecimal timeVal = new BigDecimal(cdOrder.getValue());
                    consuMessage.setTime(timeVal.multiply(new BigDecimal(3600)).longValue());
                    break;
                case "3":
                    BigDecimal dlVal = new BigDecimal(cdOrder.getValue());
                    consuMessage.setDl(dlVal.multiply(new BigDecimal(100)).longValue());
                    break;
            }
            int i = serverToClient.startCmd(consuMessage);
            if (i != 200) {
                logger.info("开启充电指令失败====" + i);
                //支付状态（0：未冻结，1：已冻结 2：成功 3：失败）
                cdOrder.setPayState("3");
                //充电状态（0:充电中 1:未充电 2:已完成,3:关闭）
                cdOrder.setChargeState("3");
                //结束方式：通讯故障
                cdOrder.setEndType("20");
                scanCodeService.updatePayStateAndGunYC(cdOrder);

                //推送通知到app，并且插入推送历史表
                CdzAppUser user = cdzAppUserService.selectByPrimaryKey(cdOrder.getUserId());
                List<Object> registrationIdList = new ArrayList<>();
                if (StringUtils.isNotEmpty(user.getDeviceId())){
                    registrationIdList.add(user.getDeviceId());
                    String title = "充电订单已完成";
                    String body = "您的充电订单已关闭，点击查看详情。";
                    String url = "/h5/chargingPile/orderDetail.html?id=" + id;
                    try {
                        PushUtil.msgPush(registrationIdList, title, body, "1",url,user.getType());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    //插入推送数据
                    CdzPush push = new CdzPush();
                    push.setTitle(title);
                    push.setContent(body);
                    push.setUrl(url);
                    push.setUserId(user.getId());
                    //获取时间
                    ZoneId zoneId = ZoneId.systemDefault();
                    LocalDateTime localDateTime = LocalDateTime.now();
                    ZonedDateTime zdt = localDateTime.atZone(zoneId);
                    Date date = Date.from(zdt.toInstant());
                    push.setPushTime(date);
                    cdzPushService.save(push);
                }
                return error(-1, "开启充电指令失败");
            } else {
                logger.info("开启充电指令成功====");
                //处理开启成功业务
                cdzAmountDetailService.startChargeBusiness(cdOrder);
                return success(200);
            }
        }else{
            return error(-1,"抱歉，充电桩下发指令失败，请换其他桩重试。");
        }

    }
    
    
    
    
    public boolean isopen(String  deviceNo){
    	logger.info("桩号----------:"+deviceNo);
    	if(StringUtils.isEmpty(deviceNo)){
    		return false;
    	}
    	Channel channel = ChannelMap.getChannelByName(deviceNo);
    	if(channel == null){
    		return false;
    	}
    	boolean con = channel.isConnected();
    	boolean op = channel.isOpen();
    	if(con && op){
    		return true;
    	}
    	logger.info("桩号----------:"+deviceNo+"-->链接状态:"+con+"-->开启状态:"+op);
		return false;
    	
    }
    
    /**
     * 生成22位交易流水号
     *
     * @return
     */
    public  String generateOrder() {
        String order = "";
        try {
            order = format.format(new Date()) + (int) ((Math.random() * 9 + 1) * 10000000);
        } catch (Exception e) {
            e.printStackTrace();
            order = "" + (int) ((Math.random() * 9 + 1) * 10000000) + (int) ((Math.random() * 9 + 1) * 10000000) + (int) ((Math.random() * 9 + 1) * 100000);
        }
        return order;
    }


}